﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Win32;
using System.Text;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;

namespace Pst_Backup_2013
{
    internal class Utilities
    {
        private static string _settingsBaseRegistryKey = @"Software\PST Backup 2013\";

        /// <summary>
        /// A structur describing a registry entry.
        /// </summary>
        internal struct RegistryEntry
        {
            private string _fullRegistryPath;
            private string _sourcePath;
            private string _destinationPath;
            private bool _toBackup;
            private DateTime _lastSuccessfulBackup;

            internal RegistryEntry(string sourcePath)
            {
                _fullRegistryPath = string.Empty;
                _sourcePath = sourcePath;
                _destinationPath = string.Empty;
                _toBackup = true;
                _lastSuccessfulBackup = new DateTime();
            }

            internal string FullRegistryPath { get { return _fullRegistryPath; } set { _fullRegistryPath = value; } }
            internal string SourcePath { get { return _sourcePath; } set { _sourcePath = value; } }
            internal string DestinationPath { get { return _destinationPath; } set { _destinationPath = value; } }
            internal bool ToBackup { get { return _toBackup; } set { _toBackup = value; } }
            internal DateTime LastSuccessfulBackup { get { return _lastSuccessfulBackup; } set { _lastSuccessfulBackup = value; } }
        }

        /// <summary>
        /// Log a message in the Windows Event Log.
        /// </summary>
        /// <param name="eventID">ID for this event.</param>
        /// <param name="message">Text to write in the event.</param>
        /// <param name="eventType">Type of the event. default value is "Information".</param>
        /// <param name="logName">Name of the log where to write the event. Default value is "Application".</param>
        /// <param name="sourceName">Name of the source of the event. Default value is "Pst-Backup-2013".</param>
        internal static void LogEvent(int eventID, string message, EventLogEntryType eventType = EventLogEntryType.Information, string logName = "Application", string sourceName = "Pst Backup 2013")
        {
            try
            {
                EventLog eventLog = new EventLog(logName, ".", sourceName);

                eventLog.WriteEntry(message, eventType, eventID);
            }
            catch (Exception) { }
        }

        internal static void UpdateRegistryEntries()
        {
            List<string> pstFileList = GetPstFileList();
            List<RegistryEntry> registryEntries = GetRegistryEntries();
            DeleteOutdatedRegistryEntries(registryEntries, pstFileList);
            if (IsNewlyCreatedPstFilesNeedtoBeSaved() || registryEntries.Count == 0)
                RegisterNewPstFilesInRegistry(registryEntries, pstFileList);
        }

        /// <summary>
        /// Allow to retrieve the list of all .PST files mount in this Outlook session.
        /// </summary>
        /// <returns>List of all .PST files mounted in this session. Path are in lower case.</returns>
        private static List<string> GetPstFileList()
        {
            List<string> pstFiles = new List<string>();

            try
            {
                Outlook.Stores stores = new Outlook.Application().Session.Stores;
                foreach (Outlook.Store store in stores)
                {
                    try
                    {
                        if (!string.IsNullOrEmpty(store.FilePath) && store.IsDataFileStore && !store.IsCachedExchange)
                        {
                            pstFiles.Add(store.FilePath.ToLower());
                        }
                    }
                    catch (Exception) { }
                }
            }
            catch (Exception ex)
            {
                Utilities.LogEvent(20001, "Unable to enumerate PST files.\r\n" + ex.Message, System.Diagnostics.EventLogEntryType.Error);
            }

            return pstFiles;
        }

        /// <summary>
        /// Gets all PST files that have been previously recorded in the registry for being processed by PST Backup 2013.
        /// </summary>
        /// <returns>A Lsit of <RegistryEntry>.</returns>
        private static List<RegistryEntry> GetRegistryEntries()
        {
            List<RegistryEntry> registryEntries = new List<RegistryEntry>();

            try
            {
                RegistryKey baseKey = Registry.CurrentUser.OpenSubKey(_settingsBaseRegistryKey, true);
                RegistryKey pstFilesKey;

                if (baseKey == null)
                    pstFilesKey = CreateBaseKey();
                else
                {
                    pstFilesKey = baseKey.OpenSubKey("PST Files");
                    if (pstFilesKey == null)
                        pstFilesKey = CreatePstFileKey(baseKey);
                }

                string[] subKeyNames = pstFilesKey.GetSubKeyNames();

                foreach (string subKeyName in subKeyNames)
                {
                    try
                    {
                        RegistryKey subKey = pstFilesKey.OpenSubKey(subKeyName);
                        registryEntries.Add(GetRegistryEntry(subKey));
                        subKey.Close();
                    }
                    catch (Exception) { }
                }
                pstFilesKey.Close();
                baseKey.Close();
            }
            catch (Exception) { }

            return registryEntries;
        }

        private static RegistryKey CreateBaseKey()
        {
            RegistryKey baseKey = Registry.CurrentUser.CreateSubKey(_settingsBaseRegistryKey);
            return CreatePstFileKey(baseKey);
        }

        private static RegistryKey CreatePstFileKey(RegistryKey baseKey)
        {
            return baseKey.CreateSubKey("PST Files");
        }

        /// <summary>
        /// Build a new RegistryEntry with values under the provided subKey.
        /// </summary>
        /// <param name="subKey">SubKey that contains registry values to build the new RegistryEntry.</param>
        /// <returns>Return a new RegistryEntry initialized with values contains under the subKey.</returns>
        private static RegistryEntry GetRegistryEntry(RegistryKey subKey)
        {
            RegistryEntry regEntry = new RegistryEntry("");
            try
            {
                regEntry.FullRegistryPath = subKey.Name;
                regEntry.SourcePath = subKey.GetValue("SourcePath").ToString();
                regEntry.DestinationPath = subKey.GetValue("DestinationPath").ToString();
                regEntry.ToBackup = subKey.GetValue("ToBackup").ToString().ToLower() == "true" ? true : false;
                regEntry.LastSuccessfulBackup = Convert.ToDateTime(subKey.GetValue("LastSuccessfulBackup").ToString());
            }
            catch (Exception) { }

            return regEntry;
        }

        /// <summary>
        /// Deletes Registry Entries that refers to PST files that are not mounted anymore in Outlook.
        /// </summary>
        /// <param name="registryEntries">List of all registry entries.</param>
        /// <param name="pstFileList">List of all PST files mounted in Outlook.</param>
        private static void DeleteOutdatedRegistryEntries(List<RegistryEntry> registryEntries, List<string> pstFileList)
        {
            try
            {
                List<RegistryEntry> keysToDelete = new List<RegistryEntry>();

                foreach (RegistryEntry regEntry in registryEntries)
                {
                    if (!pstFileList.Contains(regEntry.SourcePath.ToLower()))
                        keysToDelete.Add(regEntry);
                }

                foreach (RegistryEntry keyToDelete in keysToDelete)
                {
                    registryEntries.Remove(keyToDelete);
                }
            }
            catch (Exception) { }
        }

        /// <summary>
        /// Add newly created PST files into Registry.
        /// </summary>
        /// <param name="registryEntries">List of all registry entries.</param>
        /// <param name="pstFileList">List of all PST files mounted in Outlook.</param>
        private static void RegisterNewPstFilesInRegistry(List<RegistryEntry> registryEntries, List<string> pstFileList)
        {
            try
            {
                foreach (string pstFile in pstFileList)
                {
                    bool found = false;

                    foreach (RegistryEntry regEntry in registryEntries)
                    {
                        if (regEntry.SourcePath.ToLower() == pstFile.ToLower())
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        RegistryEntry regEntry = new RegistryEntry(pstFile.ToLower());
                        registryEntries.Add(regEntry);
                    }
                }

                DeleteAllRegistryEntries();
                RewriteRegistryEntries(registryEntries);
            }
            catch (Exception) { }
        }

        /// <summary>
        /// Delete all Registry Entry containing settings for PST files.
        /// </summary>
        private static void DeleteAllRegistryEntries()
        {
            try
            {
                RegistryKey baseKey = Registry.CurrentUser.OpenSubKey(_settingsBaseRegistryKey + "PST Files", true);

                string[] subKeyNames = baseKey.GetSubKeyNames();

                foreach (string subKeyName in subKeyNames)
                {
                    try
                    {
                        baseKey.DeleteSubKey(subKeyName);
                    }
                    catch (Exception) { }
                }
                baseKey.Close();
            }
            catch (Exception) { }
        }

        /// <summary>
        /// Write all Registry Entry with newly exttracted PST files List.
        /// </summary>
        /// <param name="registryEntries">All settings on PST files.</param>
        private static void RewriteRegistryEntries(List<RegistryEntry> registryEntries)
        {
            try
            {
                RegistryKey baseKey = Registry.CurrentUser.OpenSubKey(_settingsBaseRegistryKey + "PST Files", true);
                int index = 0;

                foreach (RegistryEntry regEntry in registryEntries)
                {
                    try
                    {
                        RegistryKey newKey = baseKey.CreateSubKey(index.ToString());
                        index++;
                        WriteValues(newKey, regEntry);
                    }
                    catch (Exception) { }
                }
                baseKey.Close();
            }
            catch (Exception) { }
        }

        /// <summary>
        /// Write all necessary values in a Registry Key.
        /// </summary>
        /// <param name="key">Registry Key where to write value.</param>
        /// <param name="registryEntry">Values to write.</param>
        private static void WriteValues(RegistryKey key, RegistryEntry registryEntry)
        {
            try
            {
                key.SetValue("SourcePath", (object)registryEntry.SourcePath, RegistryValueKind.String);
                key.SetValue("DestinationPath", (object)registryEntry.DestinationPath, RegistryValueKind.String);
                key.SetValue("ToBackup", (object)registryEntry.ToBackup, RegistryValueKind.String);
                key.SetValue("LastSuccessfulBackup", (object)registryEntry.LastSuccessfulBackup, RegistryValueKind.String);
            }
            catch (Exception) { }
        }

        /// <summary>
        /// Determine wheter or not newly created PST files need to be saved.
        /// </summary>
        /// <returns>True if the regkey "BackupNewlyCreatedPstFiles" is set to true or else false.</returns>
        private static bool IsNewlyCreatedPstFilesNeedtoBeSaved()
        {
            try
            {
                RegistryKey baseKey;
                baseKey = Registry.CurrentUser.OpenSubKey(@"Software\Policies\PST Backup 2013\Settings\Files And Folders", false);
                if (baseKey != null)
                {
                    object gpoPolicy = baseKey.GetValue("BackupNewlyCreatedPstFiles", "True");
                    return gpoPolicy.ToString().ToLower() != "false";
                }
                else
                {
                    baseKey = Registry.CurrentUser.OpenSubKey(@"Software\PST Backup 2013\Settings\Files And Folders", false);
                    if (baseKey != null)
                    {
                        object gpoPolicy = baseKey.GetValue("BackupNewlyCreatedPstFiles", "True");
                        return gpoPolicy.ToString().ToLower() != "false";
                    }
                }
            }
            catch (Exception) { }

            return true;
        }

    }
}
